package core

import (
	"testing"
)

func TestExpression001(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		{"#t", "#t"},
		{"#f", "#f"},
		{"(not #t)", "#f"},
		{"(not #f)", "#t"},
		{"(and #t #t)", "#t"},
		{"(and #f #t)", "#f"},
		{"(and #t #t #f)", "#f"},
		{"(or #t #t)", "#t"},
		{"(or #f #t)", "#t"},
		{"(or #t #t #f)", "#t"},
		{"(or #t #t #f)", "#t"},
		{"(or #f #f #t)", "#t"},
	}

	env := NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := NewParser(tokens)
		expr, err := parser.ParseExpression()
		obj, err := Eval(expr, env)
		if err != nil {
			t.Fatalf("parser.ParseExpression() error = %v", err)
		}
		if obj.Inspect() != tt.want {
			t.Errorf("Eval() = %v, want %v", obj.Inspect(), tt.want)
		}
	}
}

func TestExpression002(t *testing.T) {
	//input := "(define (xor x y) (or (and x (not y)) (and (not x) y)))"
	input := "(define (xor x y) (or (and x (not y)) (and (not x) y)))"
	env := NewEnvironment()
	RegisterBuiltinProcedures(env)

	lexer := NewLexer(input)
	tokens := lexer.ReadTokens()
	parser := NewParser(tokens)
	expr, err := parser.ParseExpression()
	if err != nil {
		t.Fatalf("parser.ParseExpression() error = %v", err)
	}
	obj, err := Eval(expr, env)
	if err != nil {
		t.Fatalf("Eval() error = %v", err)
	}
	want := "(procedure xor(x y))"
	if obj.Inspect() != want {
		t.Errorf("Eval() = %v, want %v", obj.Inspect(), want)
	}
}

func TestProgram001(t *testing.T) {
	tests := []struct {
		input string
		want  []string
	}{
		{"#t", []string{"#t"}},
		{"#t #f", []string{"#t", "#f"}},
		{"(define x #t) x", []string{"", "#t"}},
		{"(define x (and #t #t)) x", []string{"", "#t"}},
		{"(define x (and #t #t)) (not x)", []string{"", "#f"}},
		{"(define (f x) x) (f #f)", []string{"(procedure f(x))", "#f"}},
		{"(define (xor x y) (or (and x (not y)) (and (not x) y))) (xor #t #f) (xor #f #f) (xor #t #t)", []string{"(procedure xor(x y))", "#t", "#f", "#f"}},
	}

	env := NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := NewParser(tokens)
		exprs, err := parser.ParseProgram()
		if err != nil {
			t.Errorf("ParseProgram() error = %v", err)
			continue
		}
		for i, expr := range exprs {
			obj, err := Eval(expr, env)
			if err != nil {
				t.Errorf("Eval() error = %v", err)
				continue
			}
			if obj.Inspect() != tt.want[i] {
				t.Errorf("Eval() = %v, want %v", obj.Inspect(), tt.want[i])
			}
		}
	}
}

func TestLet(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		{"(let ((x #t)) x)", "#t"},
		{"(let ((x #f) (y #t)) (and x y))", "#f"},
		{"(let ((x #t) (y #t)) (and x y))", "#t"},
		{"(let ((x #t)) (let ((y #f)) (or x y)))", "#t"},
	}

	env := NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := NewParser(tokens)
		expr, err := parser.ParseExpression()
		if err != nil {
			t.Fatalf("parser.ParseExpression() error = %v", err)
		}
		obj, err := Eval(expr, env)
		if err != nil {
			t.Fatalf("Eval() error = %v", err)
		}
		if obj.Inspect() != tt.want {
			t.Errorf("Eval() = %v, want %v", obj.Inspect(), tt.want)
		}
	}
}
